﻿using NetTopologySuite.Geometries;
using NetTopologySuite.Simplify;
using System;
using System.Collections.Generic;
using System.Drawing;
using static System.Windows.Forms.LinkLabel;

namespace PointAsLine
{
    public static class LineSimplifier
    {
        /// <summary>
        /// 将连续的点集简化为尽可能少的直线段（Douglas-Peucker 变种）
        /// </summary>
        /// <param name="points">输入点集</param>
        /// <param name="maxDistance">中间点到直线的最大允许距离（像素）</param>
        /// <returns>简化后的线段列表</returns>
        public static Polyline SimplifySegments(Polyline points, float maxDistance = 10f)
        {
            if (points.Count < 3)
                return points;

            Polyline simplifiedSegments = new Polyline()
            {
                GroupIndex = points.GroupIndex
            };
            int startIndex = 0;
            simplifiedSegments.Add(points.Points[0]);
            while (startIndex < points.Count - 1)
            {
                // 找到最远的点 Pk，使得 P1 到 Pk 的直线与中间点的距离 ≤ maxDistance
                int k = FindFarthestPoint(points, startIndex, maxDistance);
                if (k == -1)
                {
                    // 如果没有找到满足条件的点，则直接连接 startPoint 和下一个点
                    simplifiedSegments.Add(points[startIndex + 1]);
                    startIndex++;
                }
                else
                {
                    // 将 P1 到 Pk 简化为直线段
                    var line = FitLineOLS(points, startIndex, k);
                    var knew= CornerReg.FallbackK(points,  startIndex, k);
                    if(knew != k)
                    {
                        k = knew;
                        line = FitLineOLS(points, startIndex, k);
                        knew = CornerReg.FallbackK(points,  startIndex, k);
                    }
                    var k1 = PointFFunc.Sub(points[k], points[startIndex]);
                    var rs = AdjustLineSegment(line, points, startIndex, k,maxDistance);
                    var k3 = PointFFunc.Sub(rs.B_new, rs.A_new);
                    var k4 = k1.X * k3.X + k1.Y * k3.Y;
                    if (k4> 0)
                    {
                        simplifiedSegments.Add(rs.A_new);
                        simplifiedSegments.Add(rs.B_new);
                    }
                    else
                    {
                        simplifiedSegments.Add(rs.B_new);
                        simplifiedSegments.Add(rs.A_new);
                    }
                    
                    startIndex = k;
                }
            }
            simplifiedSegments.Add(points[points.Count-1]);
            return simplifiedSegments;
        }
        

        

        public static (PointF2G A_new, PointF2G B_new) AdjustLineSegment(Line line, Polyline points, int fromIndex, int endIndex, float maxDistance = 10f)
        {
            if (fromIndex >= endIndex || endIndex > points.Count)
            {
                throw new ArgumentException("Invalid index range");
            }
            if (fromIndex < 0) fromIndex = 0;

            // 初始化min和max
            double minR = double.MaxValue;
            double maxR = double.MinValue;
            PointF2G minPoint = points[fromIndex];
            PointF2G maxPoint = points[fromIndex];

            
            for (int i = fromIndex; i <= endIndex; i++)
            {
                PointF2G P = points[i];
                double r = CalculateProjectionR(line, P);

                if (r < minR)
                {
                    minR = r;
                    minPoint = P;
                }

                if (r > maxR)
                {
                    maxR = r;
                    maxPoint = P;
                }
            }

            return (minPoint, maxPoint);
        }

        private static double CalculateProjectionR(Line line, PointF2G P)
        {
            // 计算点P在直线y=Kx+B上的投影点的x坐标
            double A = line.K;
            double B = -1;
            double C = line.B;

            double x0 = P.X;
            double y0 = P.Y;

            double denominator = A * A + B * B;
            if (denominator < 1e-10)
            {
                return 0;
            }

            double numerator = A * x0 + B * y0 + C;
            double x = x0 - A * numerator / denominator;
            return x;
        }

        public static Line FitLineOLS(Polyline points,int fromIndex,int endIndex)
        {
            int n = points.Count;
            if (n < 2)
            {
                throw new ArgumentException("至少需要 2 个点才能拟合直线");
            }

            double sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;

            for(var i= fromIndex; i<= endIndex; i++)
            {
                var p= points[i];
                sumX += p.X;
                sumY += p.Y;
                sumXY += p.X * p.Y;
                sumX2 += p.X * p.X;
            }

            // 计算斜率 k
            double denominator = n * sumX2 - sumX * sumX;
            if (Math.Abs(denominator) < 1e-10)
            {
                // 所有点几乎在一条垂直线上（斜率无穷大），无法用 y = kx + b 表示
                // 这种情况下可以返回一条垂直线 x = c
                // 但为了简单，我们仍然返回一个斜率为很大的直线
                double k1 = double.MaxValue;
                double b1 = sumY / n - k1 * (sumX / n);
                return new Line(k1, b1);
            }

            double k = (n * sumXY - sumX * sumY) / denominator;

            // 计算截距 b
            double b = (sumY - k * sumX) / n;

            return new Line(k, b);
        }


        /// <summary>
        /// 找到最远的点 Pk，使得 P1 到 Pk 的直线与中间点的距离 ≤ maxDistance
        /// </summary>
        private static int FindFarthestPoint(Polyline points, int startIndex, float maxDistance)
        {
            PointF2G p1 = points[startIndex];
            int farthestIndex = -1;
            float maxDistSquared = maxDistance * maxDistance;

            for (int k = startIndex + 1; k < points.Count; k++)
            {
                // 检查 P1 到 Pk 的直线与 P(startIndex+1) 到 P(k-1) 的距离是否都 ≤ maxDistance
                bool allPointsValid = true;
                for (int i = startIndex + 1; i < k; i++)
                {
                    float distSquared = DistanceSquaredToLine(points[i], p1, points[k]);
                    if (distSquared > maxDistSquared)
                    {
                        allPointsValid = false;
                        break;
                    }
                }

                if (allPointsValid)
                {
                    farthestIndex = k;
                }
                else
                {
                    // 如果 Pk 不满足条件，则停止搜索（因为更远的点更不可能满足）
                    break;
                }
            }

            return farthestIndex;
        }

        /// <summary>
        /// 计算点 P 到直线 (P1, P2) 的距离平方
        /// </summary>
        private static float DistanceSquaredToLine(PointF2G p, PointF2G p1, PointF2G p2)
        {
            float numerator = Math.Abs((p2.Y - p1.Y) * p.X - (p2.X - p1.X) * p.Y + p2.X * p1.Y - p2.Y * p1.X);
            float denominator = (float)Math.Sqrt((p2.Y - p1.Y) * (p2.Y - p1.Y) + (p2.X - p1.X) * (p2.X - p1.X));
            return (numerator * numerator) / (denominator * denominator);
        }
        /// <summary>
        /// 计算点 P 到直线 (P1, P2) 的距离平方
        /// $"y = {K}x + {B}";
        /// </summary>
        /// <summary>
        /// 计算点 P 到直线 (y = Kx + B) 的距离平方
        /// </summary>
        /// <param name="line">直线方程 y = Kx + B</param>
        /// <param name="p">要计算距离的点</param>
        /// <returns>点到直线的距离平方</returns>
        private static float DistanceSquaredToLine(Line line, PointF p)
        {
            // 直线方程: Kx - y + B = 0
            // 点(x0,y0)到直线Ax + By + C = 0的距离公式:
            // distance = |A*x0 + B*y0 + C| / sqrt(A² + B²)
            // 这里A = K, B = -1, C = B

            double A = line.K;
            double B = -1.0;
            double C = line.B;

            double x0 = p.X;
            double y0 = p.Y;

            // 计算分子 |A*x0 + B*y0 + C|
            double numerator = Math.Abs(A * x0 + B * y0 + C);

            // 计算分母 sqrt(A² + B²)
            double denominator = Math.Sqrt(A * A + B * B);

            // 距离平方 = (numerator / denominator)²
            double distanceSquared = (numerator * numerator) / (denominator * denominator);

            // 转换为float返回
            return (float)distanceSquared;
        }

        /// <summary>
        /// 将简化后的线段调整为尽可能垂直（或水平）
        /// </summary>
        /// <param name="segments">简化后的线段列表</param>
        /// <param name="angleThresholdDegrees">角度容差（度）</param>
        /// <returns>直角化后的线段列表</returns>
        public static List<(PointF2G, PointF2G)> StraightenSegments(
            List<(PointF2G, PointF2G)> segments,
            float angleThresholdDegrees = 10f)
        {
            List<(PointF2G, PointF2G)> straightenedSegments = new List<(PointF2G, PointF2G)>();
            if (segments.Count < 2)
                return straightenedSegments;

            // 第一条线段直接保留
            straightenedSegments.Add(segments[0]);

            for (int i = 1; i < segments.Count; i++)
            {
                (PointF2G, PointF2G) prevSegment = straightenedSegments[straightenedSegments.Count - 1];
                (PointF2G, PointF2G) currentSegment = segments[i];

                // 计算两条线段的夹角
                double angle = CornerReg. CalculateAngle(prevSegment, currentSegment);

                // 如果夹角接近 90°，则调整当前线段使其严格垂直
                if (Math.Abs(angle - 90) <= angleThresholdDegrees)
                {
                    // 调整当前线段的起点或终点，使其与上一条线段垂直
                    var adjustedSegment = AdjustToPerpendicular(prevSegment, currentSegment);
                    straightenedSegments.Add(adjustedSegment);
                }
                else
                {
                    // 否则直接保留当前线段
                    straightenedSegments.Add(currentSegment);
                }
            }

            return straightenedSegments;
        }

        

        /// <summary>
        /// 调整当前线段使其与上一条线段垂直
        /// </summary>
        private static (PointF2G, PointF2G) AdjustToPerpendicular(
            (PointF2G, PointF2G) prevSegment,
            (PointF2G, PointF2G) currentSegment)
        {
            PointF2G p1 = prevSegment.Item1;
            PointF2G p2 = prevSegment.Item2;
            PointF2G p3 = currentSegment.Item1;
            PointF2G p4 = currentSegment.Item2;

            // 计算上一条线段的方向向量
            PointF2G v1 = new PointF2G(p2.X - p1.X, p2.Y - p1.Y);

            // 计算垂直方向向量（旋转 90°）
            PointF2G perpendicularV = new PointF2G(-v1.Y, v1.X);

            // 调整当前线段的起点或终点，使其方向接近 perpendicularV
            // 这里简单地将当前线段的起点或终点移动到上一条线段的延长线上
            // 更复杂的实现可以最小化总误差
            PointF2G adjustedP3 = p3; // 可以调整 p3 或 p4
            PointF2G adjustedP4 = p4;

            // 简单实现：直接返回当前线段（实际需要更复杂的调整逻辑）
            return (adjustedP3, adjustedP4);
        }

        private static  double Dot(PointF p1, PointF other)
        {
            return p1.X * other.X + p1.Y * other.Y;
        }
        
        public static PointF Mult(PointF a, double k)
        {
            return new PointF((float)(a.X * k), (float)(a.Y * k));
        }
        public static PointF Add(PointF a, PointF b)
        {
            return new PointF(a.X + b.X, a.Y + b.Y);
        }
    }
}
